Repository navigation
feat: Add comments to content engine - #904
aXenDeveloper merged 1 commit into
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
React Doctor found 7 new issues in 5 files · 1 error & 6 warnings · score 84 / 100 (Needs work) · 4 fixed · vs Errors
6 warnings
Reviewed by React Doctor for commit |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b1e55ed2d0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| row: deleted, | ||
| }); | ||
| }; | ||
|
|
||
| if (await hasReplies(id)) { |
There was a problem hiding this comment.
Exclude deleted thread placeholders from search indexing
When a comment with replies is deleted, this update retains its body and published status in the database. Although the immediate syncContentSearch(..., { operation: "delete" }) removes its search document, search-indexer.ts does not select deletedAt, and isContentRowPublic in search-document.ts does not check it. A search rebuild—or reindexing dependent comments after an article edit—therefore publishes the deleted comment's text again in search, Discover and profile timelines. Carry the deletion state through indexing and prevent placeholders from becoming public documents.
Useful? React with 👍 / 👎.
| .select(selection) | ||
| .from(table) | ||
| .where(inArray(itemIdColumn, [...ids])), |
There was a problem hiding this comment.
Filter related translations by their publication state
For a published article with an unpublished or scheduled default-language translation, this query still loads that translation's title and slug, and the selection loop prefers it over published translations. loadRelatedContexts merges those values with the base article's published status without reading the translation's status or publication date, so contentSearchDocument exposes the draft title and URL through public comment search documents. Apply the translation publication predicate before choosing a related translation, as localized-public-service.ts does for public reads.
Useful? React with 👍 / 👎.
| queryFn: async ({ pageParam }) => | ||
| await fetchCommentsPage({ after: pageParam, articleId }), | ||
| queryKey: blogQueryKeys.comments(articleId), |
There was a problem hiding this comment.
Scope personalized comment queries to the current viewer
When an author or moderator loads a thread, its response can contain hidden comments and moderation reasons, but this cache key depends only on the article. Signing out while the article remains mounted leaves that privileged response rendered for the anonymous session: the sign-out action only removes the identity-query prefixes listed in tanstack/auth/queries.ts, which do not include blog comments, and invalidating the router does not invalidate this component's query. Include the viewer identity in the thread query key and avoid retaining the previous viewer's data when the session changes.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ce4f0382a2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| article: field.relation({ | ||
| target: () => blogPostContentType, | ||
| required: true, | ||
| onDelete: "cascade", |
There was a problem hiding this comment.
Synchronize comment search documents after localized post changes
For the newly added blog comments, the parent is a localized content type, but its editorial mutations take the syncContentLocalizedSearch branch in editorial-effects.ts. That function never calls syncContentSearchDependents; only the non-localized syncContentSearch path does. Consequently, hiding, unpublishing or deleting a blog article leaves its previously indexed comments publicly searchable in search, Discover and profile timelines, even though their thread is no longer public. Extend dependent synchronization to localized parent mutations before enabling this relationship.
Useful? React with 👍 / 👎.
| const deleted = row.deletedAt !== null && row.deletedAt !== undefined; | ||
| const owns = | ||
| update.own && | ||
| !hidden && | ||
| !deleted && |
There was a problem hiding this comment.
Select deletion state before authorizing public edits
When a comment with replies is deleted, its row remains as a placeholder, but lockSelection() in public-write-service.ts does not select deletedAt. Both findEditable and update pass that partial row to this authorization function, so deleted is always false and an author whose edit window remains open can still retrieve and modify a moderator-deleted comment through the public edit endpoints. Include the deletion column in the authorization selection so the new guard actually rejects edits to deleted placeholders.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8ea5a68984
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (result && reason) { | ||
| await tx | ||
| .update(table) | ||
| .set({ hiddenReason: reason }) |
There was a problem hiding this comment.
Preserve moderation reasons when hiding is a no-op
When a moderator has already hidden a comment, its author can still call /hide if their role allows hiding their own content: canHideContentRecord permits that action even when someone else hid the record. The visibility service returns a non-null result with changed: false, but this condition nevertheless overwrites hiddenReason with the author's supplied text. This lets authors replace the moderator's explanation without moderation permissions. Only write the reason when the visibility transition actually changed the record, or separately authorize changes to an existing moderation reason.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: edbba6f942
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| args: { | ||
| query: { | ||
| root: String(articleId), | ||
| first, | ||
| ...(after ? { after } : {}), |
There was a problem hiding this comment.
Resolve thread roots in the article's active locale
For an article published in Polish while its English translation is missing or unpublished, the article page loads successfully but this new comments request always returns 404. The thread handler in content/server/public-routes.ts calls rootService.findById(rootId, { select: [] }) without a locale, and localized-public-service.ts consequently checks only the content type's default locale (en for blog posts). Unlike the article query, this integration never supplies the reader's locale. Add locale-aware root resolution to the thread endpoint and pass the article's locale so comments remain readable on articles published only in a non-default language.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b970d6ff84
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| <div className="min-w-0 lg:col-start-2 lg:row-start-2"> | ||
| <PostComments | ||
| articleAuthorIds={articleAuthorIds} | ||
| articleHref={blogPostHref(item.friendlyUrl)} |
There was a problem hiding this comment.
Localize the article URL passed to comment links
On a non-default-language article whose translated slug differs from the default-language slug, this passes an unprefixed internal URL to PostComments. That component uses it directly in both the date's native <a> and SharePopover, neither of which applies the router's locale rewrite. Clicking or sharing a comment on /pl/blog/article/polski-slug therefore produces /blog/article/polski-slug, which resolves in the default locale and can return 404 because that translation has a different slug. Apply localizeHref with the current locale before passing this URL so both permalinks and shared links retain the article's language.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5b26465282
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| module: "content/comments", | ||
| path: "/{slug}", | ||
| args: { params: { slug: encodeURIComponent(request.slug) } }, |
There was a problem hiding this comment.
Resolve comment permalinks for authorized hidden-article readers
When an article is hidden, its author or moderator can read its comments through the new thread endpoint, but following a comment's permalink still returns 404. This first lookup uses public-service.ts's readOne, whose parent-visibility filter rejects hidden articles even for authorized readers; the subsequent posts/{id}/slug lookup also requests only a public article. Consequently, the comment URLs used by ModCP cannot open these conversations. Add an authorized hidden-parent resolution path for both lookups, keeping those responses private as the thread endpoint does.
Useful? React with 👍 / 👎.
7a536e2 to
5a2f140
Compare
- Shared comments controller and UI for Content Engine threads, drawn on the card surface - Blog notifies comment authors about replies; replies-only thread notifications - Docs: comments guides rebuilt around the shared Comments API, replies-only and article-author notification setups, refreshed screenshots - Tests for the shared comments module and replies-only threads Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5a2f140282
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (await hasReplies(id)) { | ||
| await c | ||
| .get("db") | ||
| .update(table) | ||
| .set({ deletedAt: new Date(), deletedBy: user.id }) | ||
| .where(eq(columns.id, id as never)); |
There was a problem hiding this comment.
Queue notification cleanup when retaining a deleted placeholder
When a comment with replies is deleted, this branch only updates its deletion columns and never calls recordContentNotificationCleanup, unlike the physical-delete service paths. Its notification receipts and unread counts therefore remain indefinitely, while contentNotificationAccess rejects the deleted row and renders its inbox item unavailable. For grouped notifications, deleting the latest contributing comment also makes the entire group unavailable even when other contributions remain public. Store the cleanup intent in the same transaction as the placeholder update so the existing cleanup worker removes or repairs these items.
Useful? React with 👍 / 👎.
| return c.json({ id }, 200, { "Cache-Control": "private, no-store" }); | ||
| } | ||
|
|
||
| await removeRow(row); |
There was a problem hiding this comment.
Serialize the reply check with comment deletion
If another request creates a reply after hasReplies(id) returns false but before removeRow(row) deletes the comment, the new reply's ON DELETE SET NULL parent relation is cleared and it becomes a top-level comment instead of remaining beneath a deleted placeholder. The reply-placement validation does not lock the parent, and this handler performs the existence check outside the deletion transaction, so this interleaving is permitted. Serialize the reply check and deletion with reply creation, including the ancestor-pruning path, to preserve the conversation structure.
Useful? React with 👍 / 👎.
5a2f140 to
3244eed
Compare
| }) => { | ||
| const t = useTranslations("core.global.share"); | ||
| const absoluteUrl = React.useMemo( | ||
| () => new URL(url, window.location.href).href, |
There was a problem hiding this comment.
React Doctor · react-doctor/no-unguarded-browser-global-in-render-or-hook-init (error)
window is read while React is rendering on the server, where browser globals are unavailable. Move the read into an effect or event, or provide a stable server snapshot.
Fix → Move browser-only reads into an effect or event, guard them behind a client-only render path, or use useSyncExternalStore with a stable server snapshot.
| * state, the **Reply** button, the actions menu, and the edit and reply forms | ||
| * in place. A placeholder stands in for a removed comment. | ||
| */ | ||
| export const ContentCommentItem = <TRecord,>({ |
There was a problem hiding this comment.
React Doctor · react-doctor/no-high-complexity-react-function (warning)
ContentCommentItem has cyclomatic complexity 23, cognitive complexity 23, and maximum nesting depth 2, so its React logic is hard to understand and change. Extract independent branches into components or hooks.
Fix → Extract independent render branches and state logic into focused components or hooks until the control flow is easy to follow.
| <EmptyMedia variant="icon"> | ||
| <MessageSquareIcon aria-hidden="true" /> | ||
| </EmptyMedia> | ||
| <EmptyTitle render={<h3 />}>{t("empty.title")}</EmptyTitle> |
There was a problem hiding this comment.
React Doctor · react-doctor/heading-has-content (warning)
Blind users can't use this heading to navigate because screen readers skip it empty, so add text, aria-label, or aria-labelledby.
Fix → Put readable text in every heading.
| * | ||
| * `children` adds your own menu items above the shared ones. | ||
| */ | ||
| export const ContentCommentActions = ({ |
There was a problem hiding this comment.
React Doctor · react-doctor/no-high-complexity-react-function (warning)
ContentCommentActions has cyclomatic complexity 23, cognitive complexity 23, and maximum nesting depth 2, so its React logic is hard to understand and change. Extract independent branches into components or hooks.
Fix → Extract independent render branches and state logic into focused components or hooks until the control flow is easy to follow.
| repliesInclude(child, anchorId), | ||
| ); | ||
|
|
||
| const CommentEntry = ({ |
There was a problem hiding this comment.
React Doctor · react-doctor/no-high-complexity-react-function (warning)
CommentEntry has cyclomatic complexity 23, cognitive complexity 31, and maximum nesting depth 2, so its React logic is hard to understand and change. Extract independent branches into components or hooks.
Fix → Extract independent render branches and state logic into focused components or hooks until the control flow is easy to follow.
| <EmptyMedia variant="icon"> | ||
| <MessageSquareIcon aria-hidden="true" /> | ||
| </EmptyMedia> | ||
| <EmptyTitle render={<h3 />}>{t("empty.title")}</EmptyTitle> |
There was a problem hiding this comment.
React Doctor · react-doctor/heading-has-content (warning)
Blind users can't use this heading to navigate because screen readers skip it empty, so add text, aria-label, or aria-labelledby.
Fix → Put readable text in every heading.
Improving Documentation
pnpm lint:fixto fix formatting issues before opening the PR.Description
What?
Why?